﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Runtime.CompilerServices;

namespace XmlToJson
{
    public class Convertor:IConvertor
    {
        private Сonfig _config;
        
        public Convertor(Сonfig CurrentConfig)
        {
             this._config = CurrentConfig; 
        }
        public Сonfig SetConfig
        {
            set { this._config = value; }
            get { return this._config; }
        }
        [MethodImpl(MethodImplOptions.Synchronized)]
        public string ConvertXML(string XML)
        {
            
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(XML);
            return ConvertNode(doc.DocumentElement);
        }
        private string ConvertNode(XmlNode root)
        {
  
            string JsonData = "";
            int TabIndex;
            switch (root.NodeType)
            {
                case XmlNodeType.Element:
                    {
                            switch (TotalChildnodeNumber(root))
                            {
                                case 0:
                                    {   
                                        if(root.ParentNode.NodeType!=XmlNodeType.Document)
                                        JsonData += "\"" + root.Name + "\"";
                                        JsonData += CheckAttributes(root, true);
                                        break;
                                    }
                                case 1:
                                    {
                                        if (root.FirstChild.NodeType == XmlNodeType.Text || root.FirstChild.NodeType == XmlNodeType.CDATA)
                                        {
                                            JsonData += "\"" + root.Name + ConvertNode(root.FirstChild);
                                            JsonData += CheckAttributes(root, true);
                           
                                            break;
                                        }
                                        else
                                        {
                                            if (root.ParentNode.NodeType != XmlNodeType.Document)
                                            JsonData += "\"" + root.Name +"\":{";
                                            else JsonData+="{\r\n";
                                            JsonData += CheckAttributes(root, false);
                                            for (int j = 0; j < root.ChildNodes.Count; j++)
                                                JsonData += ConvertNode(root.ChildNodes[j])+ "}";
                                            
                                            break;
                                        }
                                    }
                                default:
                                    {
                                        if (root.ParentNode.NodeType != XmlNodeType.Document)
                                        JsonData += "\"" + root.Name + "\":{";
                                        else JsonData += "{";
                                        JsonData += CheckAttributes(root, false);  
                                        for (int j = 0; j < root.ChildNodes.Count; j++)
                                           JsonData =JsonData+ ConvertNode(root.ChildNodes[j]) + ",";
                                        JsonData = JsonData.Substring(0, JsonData.Length - 1) + "}";
                                       
                                        break;
                                    }
                            }
                            break;
                    }
            
                case XmlNodeType.Text:
                    {
                        if(this._config.readTextNode)
                            JsonData += ":\"" + root.Value + "\"";
                        break;
                    }
                case XmlNodeType.Comment:
                    {   
                        if(this._config.readComment)
                        JsonData += ",/*" + root.Value + "*/";
                        break;
                    }
                case XmlNodeType.CDATA:
                    {
                        if(this._config.readCdata)
                        JsonData += ":\"" + root.Value + "\"";
                        break;
                    }
                default: 
                    {
                        break;
                    }
            } 
            return JsonData;
        }
        private string CheckAttributes(XmlNode _Root,bool UseBrackets)
        {
            string Attrib = "";
            if (_Root.Attributes.Count != 0 && this._config.readAttrib)
            {
                if(UseBrackets)
                    Attrib += ":{";
                for (int j = 0; j < _Root.Attributes.Count; j++)
                    Attrib += "\"" + _Root.Attributes[j].Name + "\":\"" + _Root.Attributes[j].Value + "\"" + ",";
                Attrib = Attrib.Substring(0, Attrib.Length -1) + "";
                if(UseBrackets)
                Attrib+="}";
            }
            return Attrib;
        }
        private int TotalChildnodeNumber(XmlNode _Root)
        {
            int number = 0;
            for (int j = 0; j < _Root.ChildNodes.Count; j++)
                switch (_Root.ChildNodes[j].NodeType)
                {
                    case XmlNodeType.Text:
                        {
                            if (this._config.readTextNode)
                                number++;
                            break;
                        }
                    case XmlNodeType.Comment:
                        {
                            if (this._config.readComment)
                                number++;
                            break;
                        }
                    case XmlNodeType.CDATA:
                        {
                            if (this._config.readTextNode)
                                number++;
                            break;
                        }
                    case XmlNodeType.Element:
                        {
                            
                            number++;
                            break;
                        }
                }
            return number;
        }

    }
}
